Goroutines 教程

1. 概述

Goroutines 是 Go 语言中实现并发编程的核心机制。它们是轻量级的线程,由 Go 运行时(runtime)管理,允许你在程序中同时执行多个任务。Goroutines 比传统的线程更高效,因为它们的创建和销毁开销很小,并且 Go 运行时会自动处理它们的调度。

2. Goroutines 的基本概念

2.1 什么是 Goroutine?

Goroutine 是一个函数或方法的执行实例,它与其他 Goroutines 并发运行。你可以将 Goroutine 看作是 Go 语言中的“轻量级线程”。

2.2 如何创建 Goroutine?

在 Go 中,你可以通过在函数调用前加上 go 关键字来创建一个 Goroutine。例如:

package main

import (
    "fmt"
    "time"
)

func printNumbers() {
    for i := 1; i <= 5; i++ {
        fmt.Println(i)
        time.Sleep(time.Millisecond * 500)
    }
}

func printLetters() {
    for i := 'a'; i <= 'e'; i++ {
        fmt.Printf("%c\n", i)
        time.Sleep(time.Millisecond * 500)
    }
}

func main() {
    go printNumbers()  // 创建一个 Goroutine 来执行 printNumbers 函数
    go printLetters()  // 创建另一个 Goroutine 来执行 printLetters 函数

    // 主 Goroutine 等待一段时间,以便其他 Goroutines 有足够的时间执行
    time.Sleep(time.Second * 3)
}

在这个例子中,printNumbers 和 printLetters 函数分别在两个不同的 Goroutines 中并发执行。主 Goroutine 通过 time.Sleep 等待一段时间,以确保其他 Goroutines 有足够的时间完成执行。

2.3 Goroutines 的调度

Go 运行时使用一种称为 M:N 调度的机制,其中 M 个 Goroutines 被映射到 N 个操作系统线程上。这意味着 Go 运行时可以高效地管理大量的 Goroutines,而不会消耗过多的系统资源。

3. 实践练习

3.1 练习:并发计算

编写一个程序,使用 Goroutines 并行计算一组数字的平方和立方。

package main

import (
    "fmt"
    "sync"
)

func square(num int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Square of %d is %d\n", num, num*num)
}

func cube(num int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Cube of %d is %d\n", num, num*num*num)
}

func main() {
    var wg sync.WaitGroup
    numbers := []int{1, 2, 3, 4, 5}

    for _, num := range numbers {
        wg.Add(2)
        go square(num, &wg)
        go cube(num, &wg)
    }

    wg.Wait()
}

在这个练习中,我们使用了 sync.WaitGroup 来等待所有的 Goroutines 完成执行。每个数字的平方和立方计算都在不同的 Goroutines 中并发执行。

3.2 练习:并发下载

编写一个程序,使用 Goroutines 并发下载多个文件。

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "sync"
)

func downloadFile(url string, wg *sync.WaitGroup) {
    defer wg.Done()
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println("Error downloading", url, ":", err)
        return
    }
    defer resp.Body.Close()

    out, err := os.Create(url[strings.LastIndex(url, "/")+1:])
    if err != nil {
        fmt.Println("Error creating file:", err)
        return
    }
    defer out.Close()

    _, err = io.Copy(out, resp.Body)
    if err != nil {
        fmt.Println("Error writing file:", err)
    }
}

func main() {
    var wg sync.WaitGroup
    urls := []string{
        "https://example.com/file1.txt",
        "https://example.com/file2.txt",
        "https://example.com/file3.txt",
    }

    for _, url := range urls {
        wg.Add(1)
        go downloadFile(url, &wg)
    }

    wg.Wait()
}

在这个练习中,我们使用 Goroutines 并发下载多个文件。每个文件的下载任务都在不同的 Goroutine 中执行,从而提高了下载效率。

4. 总结

Goroutines 是 Go 语言中实现并发编程的强大工具。通过使用 go 关键字,你可以轻松地创建并发任务,并利用 Go 运行时的调度机制来高效地管理这些任务。通过实践练习,你可以更好地理解如何使用 Goroutines 来解决实际问题。

在接下来的课程中,我们将学习如何使用 Channels 和 Select 语句来进一步控制 Goroutines 之间的通信和同步。